package application;

import java.awt.Toolkit;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.ObjectInputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;

import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;

import au.com.bytecode.opencsv.CSVReader;

import parser.ErlangParser;
import preprocessing.AvgAggregator;

import utils.config.Config;
import visual.KMLGraph;

import data.CityArea;
import data.MatrixTime;

public class Regularities {
	
	private Calendar START;
	private Calendar END;
	private CityArea AREA;
	
	private static int [] DAY_OF_WEEK;
	private static String BASE_OUT_DIR;
	private static String OUT_DIR;
	private static String VALUES_DIR;
	private static List<MatrixTime> MEAN;
	private static List<MatrixTime> MEDIAN;
	private static List<MatrixTime> STD;
	
	private final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
	
	public static void main(String[] args) throws Exception {
		Regularities reg = new Regularities();
		
		//reg.read();
		reg.loadStatistics();
		reg.launch(new GregorianCalendar(2011,Calendar.JULY,16,0,0),new GregorianCalendar(2011,Calendar.JULY,16,24,0));
		//reg.launch(Calendar.FRIDAY);
		//reg.findDayIrregularities();
		//reg.findDayPeriodIrregularities();
		//reg.findTimeIrregularities();
	}
	
	
	public Regularities() {
		Config config = new Config(this.getClass().getSimpleName()+".ser"); 
		
		START = (Calendar)config.get("START");
		END = (Calendar)config.get("END");
		
		DAY_OF_WEEK = (int[])config.get("DAY_OF_WEEK");
		
		
		AREA = new CityArea((String)config.get("AREA_NAME"), 
				   (double[][])config.get("AREA_BBOX"),
				   (String[])config.get("BASE_IN_DIRS"));
		
		System.out.println(AREA.getRadius());
		
		BASE_OUT_DIR = (String)config.get("BASE_OUT_DIR");
		
		OUT_DIR = FORMATTER.format(START.getTime())+"-"+FORMATTER.format(END.getTime())+
		 "-"+AREA.getName()+"/";
		
		VALUES_DIR = BASE_OUT_DIR + OUT_DIR + "VALUES/";
	}
	
	public void launch(Calendar start, Calendar end) throws Exception{
		START.setTime(start.getTime());
		END.setTime(end.getTime());
		
		run();
		
	}
	
	public void launch(int dayofweek) throws Exception{
		DAY_OF_WEEK = new int[1];
		DAY_OF_WEEK[0] = dayofweek;
		run();
		
	}
	
	public void read() throws Exception{
		System.out.println(VALUES_DIR);
		new File(VALUES_DIR).mkdirs();
		
		System.out.println("Start...");
		
		for(String dir: AREA.getBase_in_dirs())
			parseDir(dir,START, END, AREA);
		
		System.out.println("End...");
		
	}
	
	public void loadStatistics() throws Exception{
		MEAN = new ArrayList<MatrixTime>();
		MEDIAN = new ArrayList<MatrixTime>();
		STD = new ArrayList<MatrixTime>();
		
		File fdir = new File(VALUES_DIR);
		File[] files = fdir.listFiles();
		int max_i = 0;
		int max_j = 0;
		for(File f: files) {
			String[] elements = f.getName().split(",");
			max_i = Math.max(max_i, Integer.parseInt(elements[0]));
			max_j = Math.max(max_j, Integer.parseInt(elements[1].substring(0,elements[1].length()-".csv".length())));
		}
		
		for(int h=0;h<24;h++){
			MatrixTime mean = new MatrixTime(new double[max_i+1][max_j+1],START,END,AREA);
			MatrixTime median = new MatrixTime(new double[max_i+1][max_j+1],START,END,AREA);
			MatrixTime std = new MatrixTime(new double[max_i+1][max_j+1],START,END,AREA);
			
			for(int i=0;i<=max_i;i++)
				for(int j=0;j<=max_j;j++){
					DescriptiveStatistics ds = new DescriptiveStatistics();
					for(File f: files){ 
						String file_i = f.getName().split(",")[0];
						String file_j = f.getName().split(",")[1].substring(0,f.getName().split(",")[1].length()-".csv".length());
						if(file_i.equals(Integer.toString(i)) && (file_j.equals(Integer.toString(j)))){
							CSVReader reader = new CSVReader(new FileReader(f),',');
							List<String[]> list = reader.readAll();
							for(int l=0;l<list.size();l++)
								if(list.get(l)[0].equals(Integer.toString(h)) && Double.parseDouble(list.get(l)[1]) > 0)
									ds.addValue(Double.parseDouble(list.get(l)[1]));
						}
					}
					mean.getMat()[i][j] = ds.getMean();
					median.getMat()[i][j] = ds.getPercentile(50);
					std.getMat()[i][j] = ds.getStandardDeviation();
				}
			MEAN.add(mean);
			MEDIAN.add(median);
			STD.add(std);
		}
		
	}
	
	
	public void run() throws Exception {
		
		System.out.println(OUT_DIR);
		
		String S_DAYS = "";
		for(int i=0; i<DAY_OF_WEEK.length;i++) {
				if(DAY_OF_WEEK[i] == Calendar.MONDAY) S_DAYS = S_DAYS + "mon-";
				if(DAY_OF_WEEK[i] == Calendar.TUESDAY) S_DAYS = S_DAYS + "tue-";
				if(DAY_OF_WEEK[i] == Calendar.WEDNESDAY) S_DAYS = S_DAYS + "wed-";
				if(DAY_OF_WEEK[i] == Calendar.THURSDAY) S_DAYS = S_DAYS + "thu-";
				if(DAY_OF_WEEK[i] == Calendar.FRIDAY) S_DAYS = S_DAYS + "fri-";
				if(DAY_OF_WEEK[i] == Calendar.SATURDAY) S_DAYS = S_DAYS + "sat-";
				if(DAY_OF_WEEK[i] == Calendar.SUNDAY) S_DAYS = S_DAYS + "sun-";
		}
		
		
		System.out.println("Start...");
		
		List<MatrixTime> ets = new ArrayList<MatrixTime>();
		
		for(String dir: AREA.getBase_in_dirs())
			parseDir(dir,ets, START, END, AREA);
		
		
		System.out.println("Reading Complete...");
		
		List<MatrixTime> day_avg = AvgAggregator.day_avg(ets);
		
		KMLGraph.printMap(day_avg, MEAN, BASE_OUT_DIR+OUT_DIR+FORMATTER.format(START.getTime())+"-"+FORMATTER.format(END.getTime())+"-"+S_DAYS+"-"+"mean.kml");
		KMLGraph.printMap(day_avg, MEDIAN, BASE_OUT_DIR+OUT_DIR+FORMATTER.format(START.getTime())+"-"+FORMATTER.format(END.getTime())+"-"+S_DAYS+"-"+"median.kml");
		
		for(int l=0;l<STD.size();l++){
			MatrixTime mt = STD.get(l);
			
			for(int i=0;i<mt.getMat().length;i++)
				for(int j=0;j<mt.getMat()[i].length;j++)
					mt.getMat()[i][j] = MEAN.get(l).getMat()[i][j]+2*mt.getMat()[i][j];
		}
		
		KMLGraph.printMap(day_avg, STD, BASE_OUT_DIR+OUT_DIR+FORMATTER.format(START.getTime())+"-"+FORMATTER.format(END.getTime())+"-"+S_DAYS+"-"+"std.kml");
		
		
		System.out.println("Done");
		
		for(int i=0; i<3;i++) {
			Toolkit.getDefaultToolkit().beep();
			Thread.sleep(1000);
		}
	}
	
	
	public static void parseDir(String dir, List<MatrixTime> ets, Calendar START, Calendar END, CityArea AREA) throws Exception {
		File fdir = new File(dir);
		File[] files = fdir.listFiles();
		for(File f: files) {
			String[] elements = f.getName().split("_");
			Calendar start = ErlangParser.parseDateFileName(elements[3]);
			Calendar end = ErlangParser.parseDateFileName(elements[4]);
			
			boolean include = false;
			for(int i=0; i<DAY_OF_WEEK.length;i++) {
				if(start.get(Calendar.DAY_OF_WEEK) == DAY_OF_WEEK[i]) 
					include = true;
			}
			
		
			if(!start.before(START) && !end.after(END) && include) {
				ets.add(ErlangParser.parse(dir+f.getName(), AREA));
					
				if((start.get(Calendar.HOUR_OF_DAY) == 12) && start.get(Calendar.MINUTE) == 00)
					System.out.println(start.getTime());
					
			}
			
		}
	}
	
	public static void parseDir(String dir, Calendar START, Calendar END, CityArea AREA) throws Exception {
		File fdir = new File(dir);
		File[] files = fdir.listFiles();
		for(File f: files) {
			String[] elements = f.getName().split("_");
			Calendar start = ErlangParser.parseDateFileName(elements[3]);
			Calendar end = ErlangParser.parseDateFileName(elements[4]);
			
		
			if(!start.before(START) && !end.after(END)) {
				MatrixTime mt = ErlangParser.parse(dir+f.getName(), AREA);
				for(int i=0;i<mt.getMat().length;i++)
					for(int j=0;j<mt.getMat()[i].length;j++){
						double value = mt.getMat()[i][j];
						int h = mt.getStart().get(Calendar.HOUR_OF_DAY);
						String filename = i+","+j+".csv";
						
						//create the file if it doesn't exist
						new File(VALUES_DIR+filename).createNewFile();
						BufferedWriter bw = new BufferedWriter(new FileWriter(VALUES_DIR+filename, true));
						bw.write(h+","+value+",");
						bw.newLine();
						bw.close();
						
					}
				
					
				if((start.get(Calendar.HOUR_OF_DAY) == 12) && start.get(Calendar.MINUTE) == 00)
					System.out.println(start.getTime());
					
			}
			
		}
	}
	
	
	public void findDayIrregularities() throws Exception {
		//load avg and std
		ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"avg.ser"))));
		MatrixTime avg = (MatrixTime)oin.readObject();
		oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"std.ser"))));
		MatrixTime std = (MatrixTime)oin.readObject();
		
		double mean = AvgAggregator.avg(avg);
		double standarddev = AvgAggregator.avg(std);
		
		System.out.println("MEAN "+mean+" STD "+standarddev);
		
		
		//for each day
		Calendar currentday = new GregorianCalendar(2010,0,1);
		List<MatrixTime> ets = new ArrayList<MatrixTime>();
		for(String dir: AREA.getBase_in_dirs()){
			File fdir = new File(dir);
			File[] files = fdir.listFiles();
			
			for(File f: files) {
				
				String[] elements = f.getName().split("_");
				Calendar start = ErlangParser.parseDateFileName(elements[3]);
				Calendar end = ErlangParser.parseDateFileName(elements[4]);
				
			
				if(!start.before(START) && !end.after(END)){
					if(start.get(Calendar.DAY_OF_YEAR) > currentday.get(Calendar.DAY_OF_YEAR)){
						if(ets.size() > 0){
							//this is a new day, find if the current day is irregular
							MatrixTime m_dayavg = AvgAggregator.avg(ets);
							double dayAvg = AvgAggregator.avg(m_dayavg);
							if(dayAvg >= mean+standarddev)
								System.out.println(currentday.getTime()+" DAYMEAN: "+dayAvg+" LIMIT: "+(mean+standarddev)+"******");
							else
								System.out.println(currentday.getTime()+" DAYMEAN: "+dayAvg+" LIMIT: "+(mean+standarddev));
							
							for(int i=0;i<m_dayavg.getMat().length;i++)
								for(int j=0;j<m_dayavg.getMat()[i].length;j++){
									double limit = avg.getMat()[i][j]+2*std.getMat()[i][j];
									if(m_dayavg.getMat()[i][j] >0 && (m_dayavg.getMat()[i][j] >= limit || m_dayavg.getMat()[i][j] <= -limit))
										System.out.println("("+i+","+j+") "+m_dayavg.getMat()[i][j]+ " "+ limit);
								}
						}
						
						currentday.setTime(start.getTime());
						System.out.println("CURRENTDAY: "+currentday.getTime());
						ets = new ArrayList<MatrixTime>();
					}
					
					ets.add(ErlangParser.parse(dir+f.getName(), AREA));
				}
			}	
		}
		

		
	}
	
	
	public void findDayPeriodIrregularities() throws Exception {
		//load avg and std
		MatrixTime [] avgs = new MatrixTime[4];
		MatrixTime [] stds = new MatrixTime[4];
		ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"day_avg_0.ser"))));
		avgs[0] = (MatrixTime)oin.readObject();
		oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"day_avg_1.ser"))));
		avgs[1] = (MatrixTime)oin.readObject();
		oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"day_avg_2.ser"))));
		avgs[2] = (MatrixTime)oin.readObject();
		oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"day_avg_3.ser"))));
		avgs[3] = (MatrixTime)oin.readObject();
		
		oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"day_std_0.ser"))));
		stds[0] = (MatrixTime)oin.readObject();
		oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"day_std_1.ser"))));
		stds[1] = (MatrixTime)oin.readObject();
		oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"day_std_2.ser"))));
		stds[2] = (MatrixTime)oin.readObject();
		oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"day_std_3.ser"))));
		stds[3] = (MatrixTime)oin.readObject();
		
		double [] one_avg = new double[4];
		double [] one_std = new double[4];
		
		for(int i=0;i<4;i++){
			one_avg[i] = AvgAggregator.avg(avgs[i]);
			one_std[i] = AvgAggregator.avg(stds[i]);
		}
		
		
		//for each day
		Calendar currentday = new GregorianCalendar(2010,0,1);
		List<MatrixTime> ets = new ArrayList<MatrixTime>();
		for(String dir: AREA.getBase_in_dirs()){
			File fdir = new File(dir);
			File[] files = fdir.listFiles();
			
			for(File f: files) {
				
				String[] elements = f.getName().split("_");
				Calendar start = ErlangParser.parseDateFileName(elements[3]);
				Calendar end = ErlangParser.parseDateFileName(elements[4]);
				
			
				if(!start.before(START) && !end.after(END)){
					if(start.get(Calendar.DAY_OF_YEAR) > currentday.get(Calendar.DAY_OF_YEAR)){
						if(ets.size() > 0){
							//this is a new day, find if the current day is irregular
							List<MatrixTime> m_dayavg = AvgAggregator.day_period_avg(ets);
							for(int i=0;i<m_dayavg.size();i++){
								System.out.println(i);
								double dayAvg = AvgAggregator.avg(m_dayavg.get(i));
								if(dayAvg >= (one_avg[i]+2*one_std[i]))
									System.out.println(currentday.getTime()+" DAYMEAN: "+dayAvg+" LIMIT: "+(one_avg[i]+2*one_std[i])+"******");
								else
									System.out.println(currentday.getTime()+" DAYMEAN: "+dayAvg+" LIMIT: "+(one_avg[i]+2*one_std[i])+" AVG: "+one_avg[i]+" STD: "+one_std[i]);
							}

						}
						
						currentday.setTime(start.getTime());
						System.out.println("CURRENTDAY: "+currentday.getTime());
						ets = new ArrayList<MatrixTime>();
					}
					
					ets.add(ErlangParser.parse(dir+f.getName(), AREA));
				}
			}	
		}
		

		
	}
	
	public void findTimeIrregularities() throws Exception{
		//load avg and std
		ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File(BASE_OUT_DIR+OUT_DIR+"statistics.ser"))));
		Map<String,Object> stat = (Map<String,Object>)oin.readObject();
	
		MatrixTime avg = (MatrixTime)stat.get("avg");
		MatrixTime std = (MatrixTime)stat.get("std");
		
		List<MatrixTime> ets = new ArrayList<MatrixTime>();
		
		for(String dir: AREA.getBase_in_dirs())
			parseDir(dir,ets, START, END, AREA);
		
		List<MatrixTime> day_avg = AvgAggregator.day_avg(ets);
		for(int h=0;h<day_avg.size();h++){
			int plus = 0;
			int less = 0;
			for(int i=0;i<day_avg.get(h).getMat().length;i++)
				for(int j=0;j<day_avg.get(h).getMat()[i].length;j++){
					double limit = avg.getMat()[i][j]+2*std.getMat()[i][j];
					if(day_avg.get(h).getMat()[i][j]>limit)
						plus++;
					if(day_avg.get(h).getMat()[i][j]<-limit)
						less++;
				}
			
			System.out.println("HOUR: "+h+" # (>mean+2std):"+plus+" #(<mean-2std):"+less);
			
		}
		
	}

}
